﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using LuaInterface;
using ElementumForever.Utility;
using ElementumForever.Interfaces;
using ElementumForever.Enums;
using ElementumForever.Editer;

namespace ElementumForever
{
    public abstract class BaseEntity : IEntity
    {
        public EntityList Children { get; protected set; }
        protected Matrix _projectionMatrix;

        public BaseEntity()
        {
            Children = new EntityList();
            _projectionMatrix = Matrix.Identity;

        }

        public virtual void OnCreat()
        {
            //Alpha = 1;
            //Visiable = true;
            //LayerDepth = 0;
        }

        public virtual void OnAttach(IEntity parent)
        {
            this.Parent = parent;
        }

        public virtual void OnDetach()
        {
            this.Parent = null;
        }

        public virtual Matrix Projection//world to screen transform
        {
            get { return _projectionMatrix; }
        }

        public virtual Matrix Unprojection//screen to world transform
        {
            get
            {
                if (Parent != null)
                    return Parent.Unprojection * CoordinateHelper.ScreenToWorldTransformMatrix(this);
                return CoordinateHelper.ScreenToWorldTransformMatrix(this);
            }
        }

        public virtual void AttachChild(IEntity child)
        {
            child.OnAttach(this);
            Children.Add((BaseEntity)child);
        }

        public virtual void DetachChild(IEntity child)
        {
            child.OnDetach();
            Children.Remove((BaseEntity)child);
        }

        public virtual void SortChildren()
        {
            this.Children.Sort();
        }

        public virtual void OnDraw(GameTime gameTime, SpriteBatch spriteBatch)
        {
            DrawSprite(gameTime, spriteBatch);
            Children.OnDraw(gameTime, spriteBatch);
        }

        public virtual void Dispose() { }

        public virtual void OnUpdate(GameTime gameTime)
        {
            Children.OnUpdate(gameTime);
            UpdateTransform();
        }

        protected virtual void UpdateTransform()
        {
            Matrix parentTransform = Matrix.Identity;
            if (Parent != null)
                parentTransform = Parent.Projection;
            _projectionMatrix = CoordinateHelper.ChildToParentTransformMatrix(this) * parentTransform;
        }

        abstract public IEntity Clone();

        public virtual EObjectClass ObjectClass { get; set; }
        public virtual float Width { get; set; }
        public virtual float Height { get; set; }
        public virtual float ScaleX { get; set; }
        public virtual float ScaleY { get; set; }
        public virtual float OriginX { get; set; }
        public virtual float OriginY { get; set; }
        public virtual float Rotation { get; set; }
        public virtual float X { get; set; }
        public virtual float Y { get; set; }
        public virtual string Name { get; set; }
        public virtual float LayerDepth { get; set; }
        public virtual float Alpha { get; set; }
        public virtual bool Visiable { get; set; }
        public virtual Color Color { get; set; }
        public virtual IEntity Parent { get; protected set; }

        public virtual bool MechClass(string _class)
        {
            return ObjectClass.ToString() == _class;
        }

        //point in screen
        public virtual bool IsContain(Vector2 point)
        {
            if (Parent != null)
                point = CoordinateHelper.FromScreenToScene(point, Parent);//point in parent
            return IsContain(this, point);
        }

        /// <summary>
        /// get the entity local position by given point in screen
        /// </summary>
        /// <param name="point">point in screen</param>
        /// <returns>point in entity local position</returns>
        public virtual Vector2 GetLocalPosition(Vector2 point)
        {
            if (Parent != null)
                point = CoordinateHelper.FromScreenToScene(point, Parent);//point in parent
            return GetLocalPosition(this, point);
        }

        //point in parent
        private Vector2 GetLocalPosition(IEntity entity, Vector2 point)
        {
            Vector2 pointWorld = ToEntityCoordinate(entity, point);
            Vector2 local = pointWorld - GetTopLeftInParent(entity);
            return local;
        }

        public virtual EntityInfo Serialize()
        {
            return EntitySerializer.SerializeEntity(this);
        }

        public virtual void Build(IsceneBuilder builder, LuaTable define)
        {
            builder.InitializeEntity(this, define);
        }

        public virtual BaseEntity PickChildren(Vector2 touch)
        {
            SortChildren();
            foreach (BaseEntity entity in Children)
            {
                if (entity.IsContain(touch))
                    return entity;
            }
            return null;
        }

        protected abstract void Draw(GameTime gameTime, SpriteBatch spriteBatch);

        /// <summary>
        /// get top-left point of the entity 
        /// </summary>
        /// <param name="entity"></param>
        /// <returns>top-left point of the entity,entity world coordinate</returns>
        private Vector2 GetTopLeftInParent(IEntity entity)
        {
            Vector2 baseTopLeft = GetCenterInParent(entity) - new Vector2(entity.OriginX, entity.OriginY);
            return baseTopLeft;
        }

        protected void DrawSprite(GameTime gameTime, SpriteBatch spriteBatch)
        {
            spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.None, RasterizerState.CullCounterClockwise, null, Projection);
            Draw(gameTime, spriteBatch);
            spriteBatch.End();
        }

        private Vector2 GetCenterInParent(IEntity entity)
        {
            Vector2 origin = new Vector2(entity.X, entity.Y);
            return origin;
        }

        //point in parent coordinate
        private static bool IsContain(IEntity entity, Vector2 point)
        {
            point = ToEntityCoordinate(entity, point);
            Vector2 origin = new Vector2(entity.OriginX, entity.OriginY);
            //Console.WriteLine(point);
            float width = entity.Width;
            float height = entity.Height;
            float angle = entity.Rotation;

            float top = entity.Y - origin.Y;
            float down = top + height;
            float left = entity.X - origin.X;
            float right = left + width;

            if (point.X < right && point.X > left && point.Y > top && point.Y < down)
                return true;
            return false;
        }

        //point in parent
        private static Vector2 ToEntityCoordinate(IEntity entity, Vector2 point)
        {
            Vector2 pos = new Vector2();
            Matrix finalTransform = CoordinateHelper.ParentToChildTransformMatrix(entity);
            pos = Vector2.Transform(point, finalTransform);
            return pos;
        }



    }
}
